Geostatistics Example
Toy dataset from Blangiardo and Cameletti (2015).
# Plot the data.
plot(s2 ~ s1, col = rgb(SPDEtoy$y / max(SPDEtoy$y), 0, 0), data = SPDEtoy,
pch = 19, asp = 1, main = 'Toy Data')

# Create a mesh for the SPDE method and then plot it.
toy_mesh <- inla.mesh.2d(as.matrix(SPDEtoy[,c('s1', 's2')]),
max.edge = c(0.1, 0.2))
plot(toy_mesh, asp = 1)
points(SPDEtoy$s1, SPDEtoy$s2, col = rgb(SPDEtoy$y / max(SPDEtoy$y), 0, 0, 0.5),
pch = 20)

# SPDE projector matrix for estimation.
A_est <- inla.spde.make.A(toy_mesh, as.matrix(SPDEtoy[,c('s1', 's2')]))
# Initialize exponential covariance structure for SPDE.
spde <- inla.spde2.matern(mesh = toy_mesh, alpha = 2)
# Set up stack for estimation.
stack_index <- inla.spde.make.index(name = 'spatial_field', n.spde = spde$n.spde)
stack_est <- inla.stack(data = list(y = SPDEtoy$y), A = list(A_est), effects = list(c(stack_index, list(intercept = 1))), tag = 'est')
# Create a grid for prediction.
toy_nx <- 50
toy_ny <- 50
toy_grid <- expand.grid(x = seq(0, 1, length.out = toy_nx), y = seq(0, 1, length.out = toy_ny))
# SPDE projector matrix for prediction.
A_pred <- inla.spde.make.A(mesh = toy_mesh, loc = as.matrix(toy_grid))
# Set up stacks for prediction.
stack_latent <- inla.stack(data = list(xi = NA), A = list(A_pred), effects = list(stack_index), tag = 'pred_latent')
stack_response <- inla.stack(data = list(y = NA), A = list(A_pred), effects = list(c(stack_index, list(intercept = 1))), tag = 'pred_response')
# Join all three stacks.
stacks <- inla.stack(stack_est, stack_latent, stack_response)
# Fit the model with INLA.
toy_fit <- inla(
y ~ -1 + intercept + f(spatial_field, model = spde),
data = inla.stack.data(stacks),
control.predictor = list(A = inla.stack.A(stacks), compute = TRUE)
)
# Output posterior summaries.
toy_fit$summary.fixed
toy_fit$summary.hyperpar
# Extract posterior mean of latent spatial field.
index_latent <- inla.stack.index(stacks, tag = 'pred_latent')$data
post_mean <- toy_fit$summary.linear.predictor[index_latent, 'mean']
post_sd <- toy_fit$summary.linear.predictor[index_latent, 'sd']
# Plot the posterior mean and SD of the latent spatial field.
plot(im(matrix(post_mean, nrow = toy_nx, ncol = toy_ny), xrange = range(toy_grid$x), yrange = range(toy_grid$y)), main = 'Posterior Mean of Spatial Field')

plot(im(matrix(post_sd, nrow = toy_nx, ncol = toy_ny), xrange = range(toy_grid$x), yrange = range(toy_grid$y)), main = 'Posterior SD of Spatial Field')

Bei Dataset
Example from Møller and Waagepetersen (2007), Beilschmiedia pendula Lauraceae locations in a plot in Panama. bei dataset in spatstat (Baddeley and Turner 2005).
# Plot the full point pattern.
plot(bei, pch = '.', cols = 'black', main = 'Realized Point Pattern')

bei_corners <- vertices.owin(Window(bei))
bei_domain <- cbind(bei_corners$x, bei_corners$y)
bei_full_mesh <- inla.mesh.2d(cbind(bei$x, bei$y),
cutoff = 50, max.edge = c(50, 100),
loc.domain = bei_domain)
plot(bei_full_mesh, asp = 1)
plot(Window(bei), border = 'blue', add = TRUE)
points(bei, pch = '.', col = 'blue')

bei_full_spdf <- as.SpatialPoints.ppp(bei)
# CHECK PRIORS!
matern_full <- inla.spde2.pcmatern(bei_full_mesh,
prior.sigma = c(0.1, 0.99),
prior.range = c(5, 0.01))
cmp_full <- coordinates ~ mySmooth(map = coordinates, model = matern_full) + Intercept
bei_full_lgcp <- lgcp(cmp_full, bei_full_spdf)
lambda_full <- predict(bei_full_lgcp, pixels(bei_full_mesh), ~ exp(mySmooth + Intercept))
# Plot posterior means and posterior sd.
plot(lambda_full)
plot(Window(bei), border = 'white', add = TRUE)
points(bei, pch = '.', col = 'white')

plot(lambda_full['sd'])
plot(Window(bei), border = 'white', add = TRUE)
points(bei, pch = '.', col = 'white')

# Take a sample of quadrats and plot the observed point pattern.
set.seed(84323)
n_quads <- 10
botleft <- cbind(runif(n_quads, 0, 950), runif(n_quads, 0, 450))
bei_interior <- lapply(seq_len(nrow(botleft)), function(r){return(
cbind(
botleft[r, 1] + c(0, 0, 50, 50),
botleft[r, 2] + c(0, 50, 50, 0)
)
)})
bei_win <- do.call(
union.owin,
apply(botleft, 1, function(x){return(
owin(x[1] + c(0, 50), x[2] + c(0, 50))
)})
)
bei_hole <- bei[complement.owin(bei_win)]
bei_samp <- bei[bei_win]
bei_window_full <- Window(bei)
plot(bei_hole, main = 'Observed Subregion', pch = '.', cols = 'black')

bei_hole_mesh <- inla.mesh.2d(cbind(bei_hole$x, bei_hole$y),
cutoff = 50, max.edge = c(50, 100),
loc.domain = bei_domain)
plot(bei_hole_mesh, asp = 1)
plot(Window(bei), border = 'blue', add = TRUE)
points(bei_hole, pch = '.', col = 'blue')

bei_hole_spdf <- as.SpatialPoints.ppp(bei_hole)
# CHECK PRIORS!
matern_hole <- inla.spde2.pcmatern(bei_hole_mesh,
prior.sigma = c(0.1, 0.99),
prior.range = c(5, 0.01))
cmp_hole <- coordinates ~ mySmooth(map = coordinates, model = matern_hole) + Intercept
bei_hole_lgcp <- lgcp(cmp_hole, bei_hole_spdf)
lambda_hole <- predict(bei_hole_lgcp, pixels(bei_hole_mesh), ~ exp(mySmooth + Intercept))
# Plot posterior means and posterior sd.
plot(lambda_hole)
plot(Window(bei_hole), border = 'white', add = TRUE)
points(bei_hole, pch = '.', col = 'white')

plot(lambda_hole['sd'])
plot(Window(bei_hole), border = 'white', add = TRUE)
points(bei_hole, pch = '.', col = 'white')

plot(bei_window_full, main = 'Observed Sample')
plot(bei_win, add = TRUE)
plot(bei_samp, pch = '.', cols = 'black', add = TRUE)

bei_samp_mesh <- inla.mesh.2d(cbind(bei_samp$x, bei_samp$y),
cutoff = 50, max.edge = c(50, 100),
loc.domain = bei_domain)
plot(bei_samp_mesh, asp = 1)
plot(Window(bei), border = 'blue', add = TRUE)
plot(Window(bei_samp), border = 'blue', add = TRUE)
points(bei_samp, pch = '.', col = 'blue')

bei_samp_spdf <- as.SpatialPoints.ppp(bei_samp)
# CHECK PRIORS!
matern_samp <- inla.spde2.pcmatern(bei_samp_mesh,
prior.sigma = c(0.1, 0.99),
prior.range = c(5, 0.01))
cmp_samp <- coordinates ~ mySmooth(map = coordinates, model = matern_samp) + Intercept
bei_samp_lgcp <- lgcp(cmp_samp, bei_samp_spdf)
lambda_samp <- predict(bei_samp_lgcp, pixels(bei_samp_mesh), ~ exp(mySmooth + Intercept))
# Plot posterior means and posterior sd.
plot(lambda_samp)
plot(Window(bei), border = 'white', add = TRUE)
plot(Window(bei_samp), border = 'white', add = TRUE)
points(bei_samp, pch = '.', col = 'white')

plot(lambda_samp['sd'])
plot(Window(bei), border = 'white', add = TRUE)
plot(Window(bei_samp), border = 'white', add = TRUE)
points(bei_samp, pch = '.', col = 'white')

LS0tCnRpdGxlOiAiU3BhdGlhbCBQcmVkaWN0aW9uIHdpdGggSU5MQSIKYXV0aG9yOiAiS2VubmV0aCBBLiBGbGFnZyIKYmlibGlvZ3JhcGh5OiAiLi4vcmVmZXJlbmNlcy5iaWIiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgZmlnX2hlaWdodDogNgogICAgZmlnX3dpZHRoOiAxMAogICAgZmlnX2Nyb3A6IEZBTFNFCiAgICBoZWlnaHQ6ICI5NjBweCIKICAgIHdpZHRoOiAiNzIwcHgiCiAgICBzZWxmX2NvbnRhaW5lZDogVFJVRQotLS0KCgpgYGB7ciBzZXR1cCwgY2FjaGUgPSBGQUxTRSwgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGNhY2hlID0gRkFMU0UsIGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UsCiAgbWVzc2FnZSA9IEZBTFNFLCBkcGkgPSAxNTAsIGZpZy5hbGlnbiA9ICdjZW50ZXInKQpgYGAKCmBgYHtyIHBhY2thZ2VzLCBjYWNoZSA9IEZBTFNFLCBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQpsaWJyYXJ5KHNwYXRzdGF0KQpsaWJyYXJ5KElOTEEpCmxpYnJhcnkoaW5sYWJydSkKbGlicmFyeShtYXB0b29scykKYGBgCgoKIyBJbnRyb2R1Y3Rpb24KClRoaXMgdmlnbmV0dGUgaWxsdXN0cmF0ZXMgdGhlIHVzZSBvZiBJTkxBIGZvciBzcGF0aWFsIHByZWRpY3Rpb24gdXNpbmcgZXhhbXBsZXMKZnJvbSBAcmlubGEgYW5kIEBpbGxpYW5ldGFsLiBGb3IgcHJlZGljdGlvbiBvZiBjb250aW51b3VzIHNwYXRpYWwgcHJvY2Vzc2VzLAp0aGUgc3RvY2hhc3RpYyBwYXJ0aWFsIGRpZmZlcmVudGlhbCBlcXVhdGlvbnMgKFNQREUpIGFwcHJvYWNoIGlzIHVzZWQgdG8KYXBwcm94aW1hdGUgdGhlIHByb2Nlc3MgdGhyb3VnaCBhbiBhcmVhbCBHYXVzc2lhbiBNYXJrb3YgcmFuZG9tIGZpZWxkIChHTVJGKQpyZXByZXNlbnRhdGlvbi4KCgojIEdNUkYgQmFja2dyb3VuZAoKQHJpbmxhIHNlY3Rpb24gNi4xLgoKLSBPYnNlcnZhdGlvbnMgYWdncmVnYXRlZCB0byBkaXNqb2ludCBhcmVhbCByZWdpb25zIGluZGV4ZWQgYnkgJGkkLgotIEVhY2ggcmVnaW9uIGhhcyB1bmlxdWUgcGFyYW1ldGVyICRcdGhldGFfe2l9JC4KLSAkXG1hdGhjYWx7Tn0oaSkkIGlzIHRoZSBzZXQgb2YgaW5kaWNlcyBvZiBuZWlnaGJvcnMgb2YgcmVnaW9uICRpJCBhbmQKICAkXG1hdGhjYWx7Tn1fe2l9ID0gfFxtYXRoY2Fse059KGkpfCQgaXMgdGhlIG51bWJlciBvZiBuZWlnaGJvciBvZiByZWdpb24gJGkkLgotIExvY2FsIE1hcmtvdiBwcm9wZXJ0eTogZ2l2ZW4gJFxib2xkc3ltYm9se1x0aGV0YX1fe1xtYXRoY2Fse059KGkpfSQsCiAgJFx0aGV0YV97aX0kIGlzIGluZGVwZW5kZW50IG9mIGFsbCBvdGhlciAkXHRoZXRhX3tqfSQuCi0gVGhlbiB0aGUgcHJlY2lzaW9uIG1hdHJpeCAkXG1hdGhiZntRfSQgb2YgJFxib2xkc3ltYm9se1x0aGV0YX0kIGlzIHNwYXJzZQogIGJlY2F1c2Ugb25seSBuZWlnaGJvcnMgaGF2ZSBub256ZXJvIGNvcHJlY2lzaW9ucy4KCi0gQmVzYWctWW9yay1Nb2xsaSYjeDAwZTg7IG1vZGVsOgogICAgLSBFeGNoYW5nZWFibGUgcmFuZG9tIGVmZmVjdHMgJHVfe2l9JCB3aXRoIGludHJpbnNpYyBjb25kaXRpb25hbAogICAgICBhdXRvcmVncmVzc2l2ZSAoaUNBUikgc3RydWN0dXJlLgogICAgLSAkdV97aX18XG1hdGhiZnt1fV97LWl9IFxzaW0gXG1hdGhybXtOfVxsZWZ0KFxtdV97aX0gKyBcc3VtX3tqfSBhX3tpan0gKHVfe2p9IC0gXG11X3tqfSkgLyBcbWF0aGNhbHtOfV97aX0sIFxzaWdtYV97dX1eezJ9IC8gXG1hdGhjYWx7Tn1fe2l9XHJpZ2h0KSQKICAgIC0gKFdoYXQgYXJlIHRoZSAkYV97aWp9PyQpLgogICAgLSBpQ0FSIGlzIGFuIGltcHJvcGVyIHByaW9yIGJlY2F1c2UgY292YXJpYW5jZSBtYXRyaXggbm90IHBvc2l0aXZlIGRlZmluaXRlCiAgICAgIGJ1dCB0aGlzIGlzIG9rIGZvciByYW5kb20gZWZmZWN0cy4KCgojIFNQREUgQmFja2dyb3VuZAoKKkZpbmQgTGluZGdyZW4gZXQuIGFsLiAoMjAxMSkgYW5kIGZpbGwgaW4gbW90aXZhdGlvbioKCi0gU3BhdGlhbCBwcm9jZXNzICRceGkoXG1hdGhiZntzfSkkLgoKLSBTb2x2ZSAkKFxrYXBwYV57Mn0gLSBcRGVsdGEpXntcYWxwaGEgLyAyfShcdGF1IFx4aShcbWF0aGJme3N9KSkgPSBcbWF0aGNhbHtXfShcbWF0aGJme3N9KSQuCiAgICAtICRca2FwcGEkIGlzIGEgcmFuZ2UgcGFyYW1ldGVyLgogICAgLSAkXERlbHRhJCBpcyB0aGUgTGFwbGFjaWFuLgogICAgLSAkXGFscGhhJCBpcyBhIHNtb290aG5lc3MgcGFyYW1ldGVyLgogICAgLSAkXHRhdSQgYSBwcmVjaXNpb24gcGFyYW1ldGVyLgogICAgLSBFeGFjdCBhbmQgc2F0aW9uYXJ5IHNvbHV0aW9uOiAkXHhpKFxtYXRoYmZ7c30pJCBpcyBhIEdhdXNzaWFuIGZpZWxkIHdpdGggTWF0JiN4MDBlODtybiBjb3ZhcmlhbmNlIGZ1bmN0aW9uLgoKLSBGaW5pdGUgZWxlbWVudCBhcHByb3hpbWF0aW9uICQkLgoKCiMgR2Vvc3RhdGlzdGljcyBFeGFtcGxlCgpUb3kgZGF0YXNldCBmcm9tIEByaW5sYS4KCmBgYHtyIHNwZGV0b3ksIGZpZy53aWR0aCA9IDYsIG91dC53aWR0aCA9ICc2MCUnfQojIFBsb3QgdGhlIGRhdGEuCnBsb3QoczIgfiBzMSwgY29sID0gcmdiKFNQREV0b3kkeSAvIG1heChTUERFdG95JHkpLCAwLCAwKSwgZGF0YSA9IFNQREV0b3ksCiAgICAgcGNoID0gMTksIGFzcCA9IDEsIG1haW4gPSAnVG95IERhdGEnKQpgYGAKCmBgYHtyIHNwZGVtZXNoLCBmaWcud2lkdGggPSA2LCBvdXQud2lkdGggPSAnNjAlJ30KIyBDcmVhdGUgYSBtZXNoIGZvciB0aGUgU1BERSBtZXRob2QgYW5kIHRoZW4gcGxvdCBpdC4KdG95X21lc2ggPC0gaW5sYS5tZXNoLjJkKGFzLm1hdHJpeChTUERFdG95WyxjKCdzMScsICdzMicpXSksCiAgICAgICAgICAgICAgICAgICAgICAgICBtYXguZWRnZSA9IGMoMC4xLCAwLjIpKQpwbG90KHRveV9tZXNoLCBhc3AgPSAxKQpwb2ludHMoU1BERXRveSRzMSwgU1BERXRveSRzMiwgY29sID0gcmdiKFNQREV0b3kkeSAvIG1heChTUERFdG95JHkpLCAwLCAwLCAwLjUpLAogICAgICAgcGNoID0gMjApCmBgYAoKYGBge3Igc3BkZWZpdH0KIyBTUERFIHByb2plY3RvciBtYXRyaXggZm9yIGVzdGltYXRpb24uCkFfZXN0IDwtIGlubGEuc3BkZS5tYWtlLkEodG95X21lc2gsIGFzLm1hdHJpeChTUERFdG95WyxjKCdzMScsICdzMicpXSkpCgojIEluaXRpYWxpemUgZXhwb25lbnRpYWwgY292YXJpYW5jZSBzdHJ1Y3R1cmUgZm9yIFNQREUuCnNwZGUgPC0gaW5sYS5zcGRlMi5tYXRlcm4obWVzaCA9IHRveV9tZXNoLCBhbHBoYSA9IDIpCgojIFNldCB1cCBzdGFjayBmb3IgZXN0aW1hdGlvbi4Kc3RhY2tfaW5kZXggPC0gaW5sYS5zcGRlLm1ha2UuaW5kZXgobmFtZSA9ICdzcGF0aWFsX2ZpZWxkJywgbi5zcGRlID0gc3BkZSRuLnNwZGUpCnN0YWNrX2VzdCA8LSBpbmxhLnN0YWNrKGRhdGEgPSBsaXN0KHkgPSBTUERFdG95JHkpLCBBID0gbGlzdChBX2VzdCksIGVmZmVjdHMgPSBsaXN0KGMoc3RhY2tfaW5kZXgsIGxpc3QoaW50ZXJjZXB0ID0gMSkpKSwgdGFnID0gJ2VzdCcpCgojIENyZWF0ZSBhIGdyaWQgZm9yIHByZWRpY3Rpb24uCnRveV9ueCA8LSA1MAp0b3lfbnkgPC0gNTAKdG95X2dyaWQgPC0gZXhwYW5kLmdyaWQoeCA9IHNlcSgwLCAxLCBsZW5ndGgub3V0ID0gdG95X254KSwgeSA9IHNlcSgwLCAxLCBsZW5ndGgub3V0ID0gdG95X255KSkKCiMgU1BERSBwcm9qZWN0b3IgbWF0cml4IGZvciBwcmVkaWN0aW9uLgpBX3ByZWQgPC0gaW5sYS5zcGRlLm1ha2UuQShtZXNoID0gdG95X21lc2gsIGxvYyA9IGFzLm1hdHJpeCh0b3lfZ3JpZCkpCgojIFNldCB1cCBzdGFja3MgZm9yIHByZWRpY3Rpb24uCnN0YWNrX2xhdGVudCA8LSBpbmxhLnN0YWNrKGRhdGEgPSBsaXN0KHhpID0gTkEpLCBBID0gbGlzdChBX3ByZWQpLCBlZmZlY3RzID0gbGlzdChzdGFja19pbmRleCksIHRhZyA9ICdwcmVkX2xhdGVudCcpCnN0YWNrX3Jlc3BvbnNlIDwtIGlubGEuc3RhY2soZGF0YSA9IGxpc3QoeSA9IE5BKSwgQSA9IGxpc3QoQV9wcmVkKSwgZWZmZWN0cyA9IGxpc3QoYyhzdGFja19pbmRleCwgbGlzdChpbnRlcmNlcHQgPSAxKSkpLCB0YWcgPSAncHJlZF9yZXNwb25zZScpCgojIEpvaW4gYWxsIHRocmVlIHN0YWNrcy4Kc3RhY2tzIDwtIGlubGEuc3RhY2soc3RhY2tfZXN0LCBzdGFja19sYXRlbnQsIHN0YWNrX3Jlc3BvbnNlKQoKIyBGaXQgdGhlIG1vZGVsIHdpdGggSU5MQS4KdG95X2ZpdCA8LSBpbmxhKAogIHkgfiAtMSArIGludGVyY2VwdCArIGYoc3BhdGlhbF9maWVsZCwgbW9kZWwgPSBzcGRlKSwKICBkYXRhID0gaW5sYS5zdGFjay5kYXRhKHN0YWNrcyksCiAgY29udHJvbC5wcmVkaWN0b3IgPSBsaXN0KEEgPSBpbmxhLnN0YWNrLkEoc3RhY2tzKSwgY29tcHV0ZSA9IFRSVUUpCikKCiMgT3V0cHV0IHBvc3RlcmlvciBzdW1tYXJpZXMuCnRveV9maXQkc3VtbWFyeS5maXhlZAp0b3lfZml0JHN1bW1hcnkuaHlwZXJwYXIKCiMgRXh0cmFjdCBwb3N0ZXJpb3IgbWVhbiBvZiBsYXRlbnQgc3BhdGlhbCBmaWVsZC4KaW5kZXhfbGF0ZW50IDwtIGlubGEuc3RhY2suaW5kZXgoc3RhY2tzLCB0YWcgPSAncHJlZF9sYXRlbnQnKSRkYXRhCnBvc3RfbWVhbiA8LSB0b3lfZml0JHN1bW1hcnkubGluZWFyLnByZWRpY3RvcltpbmRleF9sYXRlbnQsICdtZWFuJ10KcG9zdF9zZCA8LSB0b3lfZml0JHN1bW1hcnkubGluZWFyLnByZWRpY3RvcltpbmRleF9sYXRlbnQsICdzZCddCgojIFBsb3QgdGhlIHBvc3RlcmlvciBtZWFuIGFuZCBTRCBvZiB0aGUgbGF0ZW50IHNwYXRpYWwgZmllbGQuCnBsb3QoaW0obWF0cml4KHBvc3RfbWVhbiwgbnJvdyA9IHRveV9ueCwgbmNvbCA9IHRveV9ueSksIHhyYW5nZSA9IHJhbmdlKHRveV9ncmlkJHgpLCB5cmFuZ2UgPSByYW5nZSh0b3lfZ3JpZCR5KSksIG1haW4gPSAnUG9zdGVyaW9yIE1lYW4gb2YgU3BhdGlhbCBGaWVsZCcpCnBsb3QoaW0obWF0cml4KHBvc3Rfc2QsIG5yb3cgPSB0b3lfbngsIG5jb2wgPSB0b3lfbnkpLCB4cmFuZ2UgPSByYW5nZSh0b3lfZ3JpZCR4KSwgeXJhbmdlID0gcmFuZ2UodG95X2dyaWQkeSkpLCBtYWluID0gJ1Bvc3RlcmlvciBTRCBvZiBTcGF0aWFsIEZpZWxkJykKYGBgCgoKIyBCZWkgRGF0YXNldAoKRXhhbXBsZSBmcm9tIEBtb2VsbGVyd2FhZ2VwZXRlcnNlbiwgX0JlaWxzY2htaWVkaWEgcGVuZHVsYSBMYXVyYWNlYWVfIGxvY2F0aW9ucwppbiBhIHBsb3QgaW4gUGFuYW1hLiBgYmVpYCBkYXRhc2V0IGluIGBzcGF0c3RhdGAgW0BzcGF0c3RhdF0uCgpgYGB7ciBiZWlwdHN9CiMgUGxvdCB0aGUgZnVsbCBwb2ludCBwYXR0ZXJuLgpwbG90KGJlaSwgcGNoID0gJy4nLCBjb2xzID0gJ2JsYWNrJywgbWFpbiA9ICdSZWFsaXplZCBQb2ludCBQYXR0ZXJuJykKYGBgCgpgYGB7ciBiZWltZXNofQpiZWlfY29ybmVycyA8LSB2ZXJ0aWNlcy5vd2luKFdpbmRvdyhiZWkpKQpiZWlfZG9tYWluIDwtIGNiaW5kKGJlaV9jb3JuZXJzJHgsIGJlaV9jb3JuZXJzJHkpCmJlaV9mdWxsX21lc2ggPC0gaW5sYS5tZXNoLjJkKGNiaW5kKGJlaSR4LCBiZWkkeSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1dG9mZiA9IDUwLCBtYXguZWRnZSA9IGMoNTAsIDEwMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvYy5kb21haW4gPSBiZWlfZG9tYWluKQpwbG90KGJlaV9mdWxsX21lc2gsIGFzcCA9IDEpCnBsb3QoV2luZG93KGJlaSksIGJvcmRlciA9ICdibHVlJywgYWRkID0gVFJVRSkKcG9pbnRzKGJlaSwgcGNoID0gJy4nLCBjb2wgPSAnYmx1ZScpCmBgYAoKYGBge3IgYmVpZnVsbGxnY3AsIGNhY2hlID0gVFJVRX0KYmVpX2Z1bGxfc3BkZiA8LSBhcy5TcGF0aWFsUG9pbnRzLnBwcChiZWkpCiMgQ0hFQ0sgUFJJT1JTIQptYXRlcm5fZnVsbCA8LSBpbmxhLnNwZGUyLnBjbWF0ZXJuKGJlaV9mdWxsX21lc2gsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpb3Iuc2lnbWEgPSBjKDAuMSwgMC45OSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpb3IucmFuZ2UgPSBjKDUsIDAuMDEpKQpjbXBfZnVsbCA8LSBjb29yZGluYXRlcyB+IG15U21vb3RoKG1hcCA9IGNvb3JkaW5hdGVzLCBtb2RlbCA9IG1hdGVybl9mdWxsKSArIEludGVyY2VwdApiZWlfZnVsbF9sZ2NwIDwtIGxnY3AoY21wX2Z1bGwsIGJlaV9mdWxsX3NwZGYpCmxhbWJkYV9mdWxsIDwtIHByZWRpY3QoYmVpX2Z1bGxfbGdjcCwgcGl4ZWxzKGJlaV9mdWxsX21lc2gpLCB+IGV4cChteVNtb290aCArIEludGVyY2VwdCkpCgojIFBsb3QgcG9zdGVyaW9yIG1lYW5zIGFuZCBwb3N0ZXJpb3Igc2QuCnBsb3QobGFtYmRhX2Z1bGwpCnBsb3QoV2luZG93KGJlaSksIGJvcmRlciA9ICd3aGl0ZScsIGFkZCA9IFRSVUUpCnBvaW50cyhiZWksIHBjaCA9ICcuJywgY29sID0gJ3doaXRlJykKcGxvdChsYW1iZGFfZnVsbFsnc2QnXSkKcGxvdChXaW5kb3coYmVpKSwgYm9yZGVyID0gJ3doaXRlJywgYWRkID0gVFJVRSkKcG9pbnRzKGJlaSwgcGNoID0gJy4nLCBjb2wgPSAnd2hpdGUnKQpgYGAKCmBgYHtyIGJlaWhvbGV9CiMgVGFrZSBhIHNhbXBsZSBvZiBxdWFkcmF0cyBhbmQgcGxvdCB0aGUgb2JzZXJ2ZWQgcG9pbnQgcGF0dGVybi4Kc2V0LnNlZWQoODQzMjMpCm5fcXVhZHMgPC0gMTAKYm90bGVmdCA8LSBjYmluZChydW5pZihuX3F1YWRzLCAwLCA5NTApLCBydW5pZihuX3F1YWRzLCAwLCA0NTApKQpiZWlfaW50ZXJpb3IgPC0gbGFwcGx5KHNlcV9sZW4obnJvdyhib3RsZWZ0KSksIGZ1bmN0aW9uKHIpe3JldHVybigKICAgIGNiaW5kKAogICAgICBib3RsZWZ0W3IsIDFdICsgYygwLCAwLCA1MCwgNTApLAogICAgICBib3RsZWZ0W3IsIDJdICsgYygwLCA1MCwgNTAsIDApCiAgICApCiAgKX0pCmJlaV93aW4gPC0gZG8uY2FsbCgKICB1bmlvbi5vd2luLAogIGFwcGx5KGJvdGxlZnQsIDEsIGZ1bmN0aW9uKHgpe3JldHVybigKICAgIG93aW4oeFsxXSArIGMoMCwgNTApLCB4WzJdICsgYygwLCA1MCkpCiAgKX0pCikKYmVpX2hvbGUgPC0gYmVpW2NvbXBsZW1lbnQub3dpbihiZWlfd2luKV0KYmVpX3NhbXAgPC0gYmVpW2JlaV93aW5dCmJlaV93aW5kb3dfZnVsbCA8LSBXaW5kb3coYmVpKQoKcGxvdChiZWlfaG9sZSwgbWFpbiA9ICdPYnNlcnZlZCBTdWJyZWdpb24nLCBwY2ggPSAnLicsIGNvbHMgPSAnYmxhY2snKQpgYGAKCmBgYHtyIGJlaWhvbGVtZXNofQpiZWlfaG9sZV9tZXNoIDwtIGlubGEubWVzaC4yZChjYmluZChiZWlfaG9sZSR4LCBiZWlfaG9sZSR5KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3V0b2ZmID0gNTAsIG1heC5lZGdlID0gYyg1MCwgMTAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9jLmRvbWFpbiA9IGJlaV9kb21haW4pCnBsb3QoYmVpX2hvbGVfbWVzaCwgYXNwID0gMSkKcGxvdChXaW5kb3coYmVpKSwgYm9yZGVyID0gJ2JsdWUnLCBhZGQgPSBUUlVFKQpwb2ludHMoYmVpX2hvbGUsIHBjaCA9ICcuJywgY29sID0gJ2JsdWUnKQpgYGAKCmBgYHtyIGJlaWhvbGVsZ2NwLCBjYWNoZSA9IFRSVUV9CmJlaV9ob2xlX3NwZGYgPC0gYXMuU3BhdGlhbFBvaW50cy5wcHAoYmVpX2hvbGUpCiMgQ0hFQ0sgUFJJT1JTIQptYXRlcm5faG9sZSA8LSBpbmxhLnNwZGUyLnBjbWF0ZXJuKGJlaV9ob2xlX21lc2gsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpb3Iuc2lnbWEgPSBjKDAuMSwgMC45OSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpb3IucmFuZ2UgPSBjKDUsIDAuMDEpKQpjbXBfaG9sZSA8LSBjb29yZGluYXRlcyB+IG15U21vb3RoKG1hcCA9IGNvb3JkaW5hdGVzLCBtb2RlbCA9IG1hdGVybl9ob2xlKSArIEludGVyY2VwdApiZWlfaG9sZV9sZ2NwIDwtIGxnY3AoY21wX2hvbGUsIGJlaV9ob2xlX3NwZGYpCmxhbWJkYV9ob2xlIDwtIHByZWRpY3QoYmVpX2hvbGVfbGdjcCwgcGl4ZWxzKGJlaV9ob2xlX21lc2gpLCB+IGV4cChteVNtb290aCArIEludGVyY2VwdCkpCgojIFBsb3QgcG9zdGVyaW9yIG1lYW5zIGFuZCBwb3N0ZXJpb3Igc2QuCnBsb3QobGFtYmRhX2hvbGUpCnBsb3QoV2luZG93KGJlaV9ob2xlKSwgYm9yZGVyID0gJ3doaXRlJywgYWRkID0gVFJVRSkKcG9pbnRzKGJlaV9ob2xlLCBwY2ggPSAnLicsIGNvbCA9ICd3aGl0ZScpCnBsb3QobGFtYmRhX2hvbGVbJ3NkJ10pCnBsb3QoV2luZG93KGJlaV9ob2xlKSwgYm9yZGVyID0gJ3doaXRlJywgYWRkID0gVFJVRSkKcG9pbnRzKGJlaV9ob2xlLCBwY2ggPSAnLicsIGNvbCA9ICd3aGl0ZScpCmBgYAoKYGBge3IgYmVpc2FtcH0KcGxvdChiZWlfd2luZG93X2Z1bGwsIG1haW4gPSAnT2JzZXJ2ZWQgU2FtcGxlJykKcGxvdChiZWlfd2luLCBhZGQgPSBUUlVFKQpwbG90KGJlaV9zYW1wLCBwY2ggPSAnLicsIGNvbHMgPSAnYmxhY2snLCBhZGQgPSBUUlVFKQpgYGAKCmBgYHtyIGJlaXNhbXBtZXNofQpiZWlfc2FtcF9tZXNoIDwtIGlubGEubWVzaC4yZChjYmluZChiZWlfc2FtcCR4LCBiZWlfc2FtcCR5KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3V0b2ZmID0gNTAsIG1heC5lZGdlID0gYyg1MCwgMTAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9jLmRvbWFpbiA9IGJlaV9kb21haW4pCnBsb3QoYmVpX3NhbXBfbWVzaCwgYXNwID0gMSkKcGxvdChXaW5kb3coYmVpKSwgYm9yZGVyID0gJ2JsdWUnLCBhZGQgPSBUUlVFKQpwbG90KFdpbmRvdyhiZWlfc2FtcCksIGJvcmRlciA9ICdibHVlJywgYWRkID0gVFJVRSkKcG9pbnRzKGJlaV9zYW1wLCBwY2ggPSAnLicsIGNvbCA9ICdibHVlJykKYGBgCgpgYGB7ciBiZWlzYW1wbGdjcCwgY2FjaGUgPSBUUlVFfQpiZWlfc2FtcF9zcGRmIDwtIGFzLlNwYXRpYWxQb2ludHMucHBwKGJlaV9zYW1wKQojIENIRUNLIFBSSU9SUyEKbWF0ZXJuX3NhbXAgPC0gaW5sYS5zcGRlMi5wY21hdGVybihiZWlfc2FtcF9tZXNoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByaW9yLnNpZ21hID0gYygwLjEsIDAuOTkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByaW9yLnJhbmdlID0gYyg1LCAwLjAxKSkKY21wX3NhbXAgPC0gY29vcmRpbmF0ZXMgfiBteVNtb290aChtYXAgPSBjb29yZGluYXRlcywgbW9kZWwgPSBtYXRlcm5fc2FtcCkgKyBJbnRlcmNlcHQKYmVpX3NhbXBfbGdjcCA8LSBsZ2NwKGNtcF9zYW1wLCBiZWlfc2FtcF9zcGRmKQpsYW1iZGFfc2FtcCA8LSBwcmVkaWN0KGJlaV9zYW1wX2xnY3AsIHBpeGVscyhiZWlfc2FtcF9tZXNoKSwgfiBleHAobXlTbW9vdGggKyBJbnRlcmNlcHQpKQoKIyBQbG90IHBvc3RlcmlvciBtZWFucyBhbmQgcG9zdGVyaW9yIHNkLgpwbG90KGxhbWJkYV9zYW1wKQpwbG90KFdpbmRvdyhiZWkpLCBib3JkZXIgPSAnd2hpdGUnLCBhZGQgPSBUUlVFKQpwbG90KFdpbmRvdyhiZWlfc2FtcCksIGJvcmRlciA9ICd3aGl0ZScsIGFkZCA9IFRSVUUpCnBvaW50cyhiZWlfc2FtcCwgcGNoID0gJy4nLCBjb2wgPSAnd2hpdGUnKQpwbG90KGxhbWJkYV9zYW1wWydzZCddKQpwbG90KFdpbmRvdyhiZWkpLCBib3JkZXIgPSAnd2hpdGUnLCBhZGQgPSBUUlVFKQpwbG90KFdpbmRvdyhiZWlfc2FtcCksIGJvcmRlciA9ICd3aGl0ZScsIGFkZCA9IFRSVUUpCnBvaW50cyhiZWlfc2FtcCwgcGNoID0gJy4nLCBjb2wgPSAnd2hpdGUnKQpgYGAKCgojIFJlZmVyZW5jZXMKCg==